package User;

import java.io.IOException;

import java.sql.*;
import java.util.*;

import DBModule.*;
import Test.Course;
import Test.Exception.FactoryException;
import Test.Exception.IllegalFormatException;

public class UserManager{
	private static AbstractMap<Integer, PreparedStatement> getTypeMap(Connection conn, String format, User[] users) throws SQLException{
		AbstractMap<Integer, PreparedStatement> ret = new TreeMap<Integer, PreparedStatement>();
		for (User u : users) {
			int type = u.getType();
			if (ret.containsKey(type)) continue;
			PreparedStatement state = conn.prepareStatement(
					String.format(format, User.getTypeString(type)));
			ret.put(type, state);
		}
		return ret;
	}
	
	private static void closeTypeMap(AbstractMap<Integer, PreparedStatement> map) throws SQLException{
		if (map == null) return;
		Iterator<PreparedStatement> ps = map.values().iterator();
		while (ps.hasNext())
			ps.next().close();
	}
	
	private static void insertCourse(Connection conn, User... users) throws SQLException{
		String format = "insert into %s_Course(UID, CID) values(?, ?)";
		AbstractMap<Integer, PreparedStatement> typeMap = null;
		try{
			typeMap = getTypeMap(conn, format, users);
			for (User u : users){
				PreparedStatement ps = typeMap.get(u.getType());
				Iterator<Course> iter = u.getCourses();
				if (iter == null) continue;
				ps.setInt(1, u.getID());
				while (iter.hasNext()){
					ps.setInt(2, iter.next().getId());
					ps.executeUpdate();
				}
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static void deleteCourse(Connection conn, User... users) throws SQLException{
		String format = "delete from %s_Course where UID=?";
		AbstractMap<Integer, PreparedStatement> typeMap = null;
		try{
			typeMap = getTypeMap(conn, format, users);
			for (User u : users){
				PreparedStatement ps = typeMap.get(u.getType());
				ps.setInt(1, u.getID());
				ps.executeUpdate();
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static void updateUsual(Connection conn, User... users) throws SQLException{
		String format = "update %s set " +
			"name=?, password=?, dept=?, phone=?, email=? where ID=?";
	
		AbstractMap<Integer, PreparedStatement> typeMap = null;
	
		try{
			typeMap = getTypeMap(conn, format, users);
			for (User u : users){
				PreparedStatement ps = typeMap.get(u.getType());
				ps.setString(1, u.getName());
				ps.setString(2, u.getPassword());
				ps.setString(3, u.getDept());
				ps.setString(4, u.getPhone());
				ps.setString(5, u.getEmail());
				ps.setInt(6, u.getID());
				ps.executeUpdate();
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static void insertUsual(Connection conn, User... users) throws SQLException{
		String format = "insert into %s(name, password, dept, phone, email, ID) " +
			"values(?,?,?,?,?,?)";
	
		AbstractMap<Integer, PreparedStatement> typeMap = null;

		try{
			typeMap = getTypeMap(conn, format, users);
			for (User u : users){
				PreparedStatement ps = typeMap.get(u.getType());
				ps.setString(1, u.getName());
				ps.setString(2, u.getPassword());
				ps.setString(3, u.getDept());
				ps.setString(4, u.getPhone());
				ps.setString(5, u.getEmail());
				ps.setInt(6, u.getID());
				ps.executeUpdate();
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static void deleteUsual(Connection conn, User... users) throws SQLException{
		String format = "delete from %s where ID=?";
		
		AbstractMap<Integer, PreparedStatement> typeMap = null;

		try{
			typeMap = getTypeMap(conn, format, users);
			for (User u : users){
				PreparedStatement ps = typeMap.get(u.getType());
				ps.setInt(1, u.getID());
				ps.executeUpdate();
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static void queryUsual(Connection conn, User... users) throws SQLException, IOException, FactoryException {
		String format = "select name, dept, phone, email from %s where ID=? and password=?";
		AbstractMap<Integer, PreparedStatement> typeMap = null;

		try{
			typeMap = getTypeMap(conn, format, users);
			for (User user : users){
				PreparedStatement ps = typeMap.get(user.getType());
				ps.setInt(1, user.getID());
				ps.setString(2, user.getPassword());
				ResultSet rs = ps.executeQuery();
				if (!rs.next()) 
					throw new FactoryException("ID不存在或密码错误");
				user.setName(rs.getString(1));
				user.setDept(rs.getString(2));
				user.setPhone(rs.getString(3));
				user.setEmail(rs.getString(4));
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static void queryCourse(Connection conn, User... users) throws SQLException, IOException {
		String format = "select CID from %s_Course where UID=?";
		AbstractMap<Integer, PreparedStatement> typeMap = null;

		try{
			typeMap = getTypeMap(conn, format, users);
			for (User user : users){
				PreparedStatement ps = typeMap.get(user.getType());
				ps.setInt(1, user.getID());
				ResultSet rs = ps.executeQuery();
				while (rs.next())
					user.addCourse(rs.getInt(1));
			}
		}
		finally{
			closeTypeMap(typeMap);
		}
	}
	
	private static DBUpdater<User> updater = new DBUpdater<User>() {
		@Override
		public void update(Connection conn, User... users) throws SQLException,
				IOException {
			updateUsual(conn, users);
			deleteCourse(conn, users);
			insertCourse(conn, users);
		}
	};
	
	private static DBUpdater<User> inserter = new DBUpdater<User>() {
		@Override
		public void update(Connection conn, User... users) throws SQLException, IOException {
			insertUsual(conn, users);
//			deleteCourse(conn, users);	// 设置foreign key后可以少这句话
			insertCourse(conn, users);
		}
	};
	
	private static DBUpdater<User> deleter = new DBUpdater<User>() {
		@Override
		public void update(Connection conn, User... users) throws SQLException, IOException {
			deleteUsual(conn, users);
//			deleteCourse(conn, users);	// 设置cascade后可以少这句话
		}
	};
	
	
	private static DBQuerier<User> querier = new DBQuerier<User>() {
		@Override
		/**
		 * keys[0]: type
		 * keys[1]: ID
		 * keys[2]: password
		 * keys[3]: type
		 * keys[4]: ID
		 * keys[5]: password
		 * ...
		 */
		public Collection<User> query(Connection conn, Object... keys)
				throws SQLException, IOException, FactoryException {
			ArrayList<User> ret = new ArrayList<User>();
//			final User[] users = new User[keys.length/2];
			for (int i = 0; i < keys.length; i += 3){
				User user = new User();
				user.setType((Integer)keys[i]);
				user.setID((Integer)keys[i+1]);
				user.setPassword((String)keys[i+2]);
				ret.add(user);
			}
			
			User[] users = ret.toArray(new User[ret.size()]);
			
			queryUsual(conn, users);
			queryCourse(conn, users);
			
			return ret;
		}
	};
	
	public static User getUser(int type, int id, String password) throws SQLException, IOException, FactoryException{
		Iterator<User> iter = DBManager.query(querier, type, id, password).iterator();
		return iter.hasNext() ? iter.next() : null;
	}
	
	public static void registerUser(User u) throws Exception{
		u.checkFormat();
		DBManager.update(inserter, u);
	}
	
	public static void updateUser(User u) throws Exception{
		u.checkFormat();
		DBManager.update(updater, u);
	}
	
	public static void deleteUser(User u) throws Exception{
		DBManager.update(deleter, u);
	}
}











